18.4 Browser Use Demo:浏览器自动化
项目定位:基于 Playwright 的浏览器自动化参考实现,展示 Claude 的 Browser Tool API,提供比 Computer Use 更精准的 Web 交互能力。
1. 项目概述
1.1 Browser Use vs Computer Use
| 特性 | Browser Use | Computer Use |
|---|---|---|
| 定位方式 | DOM ref 引用 | 屏幕坐标 |
| 可靠性 | 高(结构化) | 中(视觉依赖) |
| 速度 | 快 | 较慢 |
| 适用范围 | 仅浏览器 | 全桌面 |
| 表单处理 | 直接设置值 | 模拟键盘 |
| 跨分辨率 | 稳定 | 需要缩放 |
1.2 核心优势
Browser Use 的关键创新是使用 DOM ref 替代坐标点击:
传统坐标方式:
Claude: "点击坐标 (512, 384)"
问题:屏幕尺寸变化时失效
DOM ref 方式:
Claude: "点击 ref='btn-submit'"
优势:无论屏幕尺寸如何,都能准确定位1.3 技术栈
浏览器引擎: Playwright + Chromium
Web 界面: Streamlit
显示系统: XVFB + NoVNC
容器化: Docker + Docker Compose
AI 引擎: Claude API (4.5 系列)2. 快速开始
2.1 环境准备
bash
# 克隆仓库
git clone https://github.com/anthropics/claude-quickstarts.git
cd claude-quickstarts/browser-use-demo
# 配置环境变量
cp .env.example .env
# 编辑 .env,添加 ANTHROPIC_API_KEY2.2 运行容器
bash
# 生产模式
docker-compose up --build
# 开发模式(文件变化自动同步)
docker-compose up --build --watch2.3 访问界面
| 端口 | 用途 | URL |
|---|---|---|
| 8080 | Streamlit 主界面 | http://localhost:8080 |
| 6080 | NoVNC 浏览器视图 | http://localhost:6080 |
| 5900 | VNC 直连 | vnc://localhost:5900 |
3. 架构详解
3.1 系统架构
┌─────────────────────────────────────────────────────────────────┐
│ Docker Container │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Streamlit Interface │ │
│ │ (Port 8080) │ │
│ └───────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Claude API + Browser Tool │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ loop.py │ ◀─────▶ │ browser.py │ │ │
│ │ │ (Agent循环) │ │ (浏览器工具) │ │ │
│ │ └─────────────┘ └──────┬──────┘ │ │
│ └─────────────────────────────────┼─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Playwright + Chromium │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Browser Instance │ │ │
│ │ │ • DOM 检查 (read_page) │ │ │
│ │ │ • 元素交互 (click, type) │ │ │
│ │ │ • 表单操作 (form_input) │ │ │
│ │ │ • 页面导航 (navigate) │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ XVFB Virtual Display │ │
│ └───────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ VNC / NoVNC Server │ │
│ │ (Ports 5900 / 6080) │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘3.2 项目结构
browser-use-demo/
├── browser_use_demo/
│ ├── __init__.py
│ ├── streamlit.py # Streamlit UI
│ ├── loop.py # Agent 循环
│ └── tools/
│ ├── browser.py # 浏览器工具(核心)
│ ├── browser_tool_utils/
│ │ ├── dom_extract.js # DOM 提取
│ │ ├── element_find.js # 元素定位
│ │ └── form_utils.js # 表单操作
│ └── coordinate_scaling.py # 坐标缩放
├── docker-compose.yml
├── Dockerfile
├── .env.example
└── README.md4. Browser Tool 详解
4.1 支持的动作
Web 专属动作(Browser Use 独有)
| 动作 | 参数 | 说明 |
|---|---|---|
navigate | url 或 "back"/"forward" | 导航到 URL 或历史操作 |
read_page | 无 | 获取 DOM 树和元素 ref |
get_page_text | 无 | 提取页面所有文本 |
find | text | 搜索并高亮页面文本 |
form_input | ref, value | 直接设置表单值 |
scroll_to | ref | 滚动到指定元素 |
execute_js | code | 执行 JavaScript |
鼠标动作(支持 ref 或坐标)
| 动作 | 参数 | 说明 |
|---|---|---|
left_click | ref 或 coordinate | 左键点击 |
right_click | ref 或 coordinate | 右键点击 |
double_click | ref 或 coordinate | 双击 |
hover | ref 或 coordinate | 悬停 |
left_click_drag | start, end | 拖拽 |
键盘动作
| 动作 | 参数 | 说明 |
|---|---|---|
type | text | 输入文本 |
key | key_name | 按键(如 "Enter", "ctrl+a") |
hold_key | key, duration | 长按按键 |
其他动作
| 动作 | 参数 | 说明 |
|---|---|---|
screenshot | 无 | 截取屏幕 |
scroll | direction, amount | 滚动页面 |
zoom | x1, y1, x2, y2 | 放大区域 |
wait | duration | 等待 |
4.2 DOM ref 机制
javascript
// browser_tool_utils/dom_extract.js(简化版)
function extractDOM() {
const elements = [];
let refCounter = 0;
function traverse(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
const ref = `el-${refCounter++}`;
node.setAttribute('data-claude-ref', ref);
elements.push({
ref: ref,
tag: node.tagName.toLowerCase(),
text: node.innerText?.slice(0, 100),
attributes: {
id: node.id,
class: node.className,
type: node.type,
placeholder: node.placeholder,
},
rect: node.getBoundingClientRect(),
isInteractive: isInteractive(node),
});
}
for (const child of node.childNodes) {
traverse(child);
}
}
traverse(document.body);
return elements;
}
function isInteractive(node) {
const interactiveTags = ['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
return (
interactiveTags.includes(node.tagName) ||
node.hasAttribute('onclick') ||
node.hasAttribute('role')
);
}4.3 坐标缩放系统
浏览器视口与 Claude 处理的图像尺寸不同,需要自动缩放:
python
# browser_use_demo/tools/coordinate_scaling.py
class CoordinateScaler:
# 浏览器视口:1920x1080 (16:9)
BROWSER_WIDTH = 1920
BROWSER_HEIGHT = 1080
# Claude 处理分辨率:1456x819 (16:9)
CLAUDE_WIDTH = 1456
CLAUDE_HEIGHT = 819
def __init__(self):
self.scale_x = self.BROWSER_WIDTH / self.CLAUDE_WIDTH
self.scale_y = self.BROWSER_HEIGHT / self.CLAUDE_HEIGHT
def to_browser(self, x: int, y: int) -> tuple[int, int]:
"""将 Claude 坐标转换为浏览器坐标"""
return (
int(x * self.scale_x),
int(y * self.scale_y)
)
def to_claude(self, x: int, y: int) -> tuple[int, int]:
"""将浏览器坐标转换为 Claude 坐标"""
return (
int(x / self.scale_x),
int(y / self.scale_y)
)5. 核心实现
5.1 Browser Tool 类
python
# browser_use_demo/tools/browser.py(简化版)
from playwright.async_api import async_playwright
class BrowserTool:
def __init__(self):
self.browser = None
self.page = None
self.scaler = CoordinateScaler()
async def initialize(self):
self.playwright = await async_playwright().start()
self.browser = await self.playwright.chromium.launch(
headless=False, # 需要可视化
args=['--no-sandbox']
)
self.page = await self.browser.new_page()
await self.page.set_viewport_size({
'width': 1920,
'height': 1080
})
async def execute(self, action: str, **params):
match action:
case 'navigate':
return await self._navigate(params['text'])
case 'read_page':
return await self._read_page()
case 'left_click':
return await self._click(params.get('ref'), params.get('coordinate'))
case 'form_input':
return await self._form_input(params['ref'], params['value'])
case 'screenshot':
return await self._screenshot()
case _:
raise ValueError(f"Unknown action: {action}")
async def _navigate(self, target: str):
if target == 'back':
await self.page.go_back()
elif target == 'forward':
await self.page.go_forward()
else:
await self.page.goto(target)
return {"success": True, "url": self.page.url}
async def _read_page(self):
# 注入 DOM 提取脚本
elements = await self.page.evaluate(DOM_EXTRACT_SCRIPT)
return {
"url": self.page.url,
"title": await self.page.title(),
"elements": elements
}
async def _click(self, ref: str = None, coordinate: tuple = None):
if ref:
# 使用 ref 定位
element = await self.page.query_selector(f'[data-claude-ref="{ref}"]')
if element:
await element.click()
return {"success": True, "method": "ref"}
elif coordinate:
# 使用坐标(已缩放)
x, y = self.scaler.to_browser(*coordinate)
await self.page.mouse.click(x, y)
return {"success": True, "method": "coordinate"}
return {"success": False, "error": "No target specified"}
async def _form_input(self, ref: str, value: str):
element = await self.page.query_selector(f'[data-claude-ref="{ref}"]')
if element:
await element.fill(value)
return {"success": True}
return {"success": False, "error": f"Element {ref} not found"}
async def _screenshot(self):
import base64
screenshot = await self.page.screenshot()
return {
"type": "image",
"media_type": "image/png",
"data": base64.b64encode(screenshot).decode()
}5.2 工具参数定义
python
BROWSER_TOOL_INPUT_SCHEMA = {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"navigate", "read_page", "get_page_text", "find",
"form_input", "scroll_to", "execute_js",
"left_click", "right_click", "double_click", "hover",
"type", "key", "hold_key",
"screenshot", "scroll", "zoom", "wait"
],
"description": "要执行的浏览器动作"
},
"ref": {
"type": "string",
"description": "目标元素的 ref 标识符"
},
"coordinate": {
"type": "array",
"items": {"type": "number"},
"description": "[x, y] 坐标"
},
"text": {
"type": "string",
"description": "文本内容(用于导航、搜索、输入等)"
},
"value": {
"type": "string",
"description": "表单值"
}
},
"required": ["action"]
}6. 使用示例
6.1 基础导航
用户:访问 news.ycombinator.com 并告诉我前3条新闻
Claude 执行流程:
1. navigate("https://news.ycombinator.com")
2. read_page() → 获取 DOM 结构
3. 分析页面内容
4. 返回前3条新闻标题和链接6.2 搜索任务
用户:在 GitHub 上搜索 "playwright python"
Claude 执行流程:
1. navigate("https://github.com")
2. read_page() → 找到搜索框 ref
3. form_input(ref="search-input", value="playwright python")
4. key("Enter")
5. wait(2000)
6. read_page() → 获取搜索结果
7. 总结搜索结果6.3 表单填写
用户:在演示表单中填写姓名和邮箱
Claude 执行流程:
1. navigate("https://example.com/form")
2. read_page() → 识别表单字段
3. form_input(ref="name-field", value="John Doe")
4. form_input(ref="email-field", value="john@example.com")
5. left_click(ref="submit-button")
6. 确认提交成功7. 与 Computer Use 的对比
7.1 能力矩阵
| 能力 | Browser Use | Computer Use |
|---|---|---|
| DOM 访问 | ✅ 结构化 | ❌ 仅视觉 |
| 元素定位 | ✅ ref 引用 | ❌ 坐标 |
| 表单填写 | ✅ 直接设置 | ❌ 模拟键盘 |
| 页面文本提取 | ✅ 精确 | ❌ OCR |
| JavaScript 执行 | ✅ | ❌ |
| 桌面应用 | ❌ | ✅ |
| 多应用切换 | ❌ | ✅ |
7.2 选择指南
使用 Browser Use 当:
✓ 任务仅涉及浏览器
✓ 需要可靠的表单处理
✓ 需要提取结构化数据
✓ 对速度有要求
使用 Computer Use 当:
✓ 需要操作桌面应用
✓ 需要跨应用工作流
✓ 浏览器自动化被检测/阻止
✓ 任务涉及非 Web 界面8. 安全最佳实践
8.1 风险与缓解
| 风险 | 缓解措施 |
|---|---|
| 凭证泄露 | 不在浏览器中输入真实密码 |
| 恶意重定向 | 限制允许访问的域名 |
| CAPTCHA | 避免触发反自动化机制 |
| 提示注入 | 警惕网页内容覆盖指令 |
8.2 域名白名单示例
python
ALLOWED_DOMAINS = [
"github.com",
"news.ycombinator.com",
"wikipedia.org",
# 添加你信任的域名
]
async def navigate(self, url: str):
from urllib.parse import urlparse
domain = urlparse(url).netloc
if domain not in ALLOWED_DOMAINS:
raise SecurityError(f"Domain {domain} not in allowlist")
await self.page.goto(url)9. 故障排除
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 浏览器不可见 | NoVNC 连接问题 | 刷新 6080 端口页面 |
| 操作失败 | 反自动化检测 | 尝试其他网站 |
| CAPTCHA 触发 | 频繁访问 | 添加随机延迟 |
| 元素找不到 | 页面未加载完 | 增加 wait 时间 |
| API 错误 | 模型不支持 | 使用 4.5 系列模型 |
10. 总结
Browser Use Demo 提供了一种更可靠的浏览器自动化方案:
| 方面 | 评价 |
|---|---|
| 可靠性 | ⭐⭐⭐⭐⭐ DOM ref 机制稳定可靠 |
| 易用性 | ⭐⭐⭐⭐ 清晰的 API 设计 |
| 性能 | ⭐⭐⭐⭐ 比 Computer Use 更快 |
| 安全性 | ⭐⭐⭐ 需要遵循最佳实践 |
适用场景:
- Web 数据抓取和提取
- 自动化测试探索
- 表单自动填写
- 网站监控
学习要点:
- DOM ref 元素定位机制
- Playwright 浏览器自动化
- 坐标缩放与映射
- Web 安全最佳实践
下一节,我们将学习 Autonomous Coding,了解如何构建能够持续编码的自主 Agent。